const path = require('path')
const fs = require('fs')
const { fileExists, padNumber } = require('../utils/common')
const baseConfig = require('../base-config')
const logger = require('../utils/console-logger')

const SubModel = require('../models/sub-model')
const ClientModel = require('../models/client-model')
const SiteModel = require('../models/btsite-model')
const CategoryModel = require('../models/category-model')

const SubService = require('../services/sub-service')

const BtClients = require('../utils/btclients/index')
const TorrentModel = require('../models/torrent-model')
const TorrentInfoParser = require('../utils/torrent/torrent-info-parser')
const torrentsPath = path.join(baseConfig.configPath, 'torrents')

const { download } = require('../utils/http-utils')
const { findMovie, findTv } = require('./Indexer.js')


const ArrayTool = require('../utils/array-tool')



let client;
let siteOptions;
let categoryOptions;

const prepare = async () => {
    const clientModels = await ClientModel.list({ sortField: 'order', sortOrder: 'asc' }, null, 1)
    if (clientModels.length) {
        client = BtClients.getClient(clientModels[0])
    }
    siteOptions = await SiteModel.list({ sortField: 'order', sortOrder: 'asc' })
    categoryOptions = await CategoryModel.list({ sortField: 'order', sortOrder: 'asc' })
    logger.info(`订阅初始化: 下载器: ${client ? clientModels[0].name : '未配置'}, 站点: ${siteOptions.length}, 分类: ${categoryOptions.length}`)
}

const action = async () => {
    const subs = await SubModel.list({ sortField: 'id', sortOrder: 'asc' }, where => {
        where.where('status','!=', 'playable').whereNotNull('libraryid').where('libraryid','!=', '')
    })
    if (subs.length) {
        // 1.先同步媒体库信息
        await SubService.syncFromMediaServer(subs)
        await prepare()
        for (let index = 0; index < subs.length; index++) {
            const sub = subs[index];
            await handelSub(sub)
        }
    }
}

// 调用前需要先调用prepare
const handelSub = async (sub) => {
    const pushOptions = {
        startNow: true,
        enableSiteFolder: false,
        enableCategoryFolder: true,
    }
    const searchOptions = sub.search ? JSON.parse(sub.search) : undefined
    const isPushToClient = !searchOptions || searchOptions.pushToClient // 未配置默认推送, 配置了则按配置
    if (sub.mediaType === 'movie') {

        // 1.查找资源
        const movie = await findMovie({ title: sub.title, year: sub.year.split('-')[0] }, searchOptions)
        // console.log('movie', movie);
        if (movie) {
            // 2.下载种子 保存信息
            const torrentModel = await saveTorrent(movie, sub)
            if (torrentModel) {
                // 3.推送到下载器
                const pushed = isPushToClient && await pushToClient(torrentModel, pushOptions)
                sub.status = pushed ? 'download' : 'find'
                await SubModel.updateBy({
                    status: sub.status,
                }, { id: sub.id })
            }
            logger.info('订阅更新结果：', sub.title, sub.status);
        }
    } else if (sub.mediaType === 'tv') {
        const seasons = JSON.parse(sub.seasons)
        for (let index = 0; index < seasons.length; index++) {
            const season = seasons[index];

            // 1.查找资源
            if (season.lack.length) {
                const tv = await findTv({ title: sub.title, year: sub.year.split('-')[0], season: season.season_number, lackEpisodes: season.lack, allEpisodesCount: season.episode_count }, searchOptions)
                // console.log('tv', tv.findEpisodes);
                // tv.torrents.forEach(item => {
                //     console.log(item);
                // });
                if (tv && tv.torrents.length) {
                    const find = [];
                    const download = [];
                    for (let index = 0; index < tv.torrents.length; index++) {
                        const torrent = tv.torrents[index];
                        // 2.下载种子 保存信息
                        const torrentModel = await saveTorrent(torrent, sub)
                        if (torrentModel) {
                            // 3.推送到下载器
                            const pushed = isPushToClient && await pushToClient(torrentModel, pushOptions)
                            pushed ? download.push(...torrent.meta.episodes) : find.push(...torrent.meta.episodes)
                        }
                    }
                    season.lack = ArrayTool.difference(season.lack, find.concat(download))
                    season.find ? season.find.push(...find) : season.find = find
                    season.download ? season.download.push(...download) : season.download = download

                    logger.info('订阅更新结果：', sub.title, season.season_number, seasons);
                    await SubModel.updateBy({
                        seasons: JSON.stringify(seasons),
                    }, { id: sub.id })
                }
            } else {
                logger.info('无需更新订阅：', sub.title, season.season_number);
            }
        }
    }
}

const siteMatch = (val) => {
    let matched
    if (val && siteOptions.length > 0) {
        const temp = splitSite(val)
        matched = siteOptions.find(s => {
            const sTemp = splitSite(s.address)
            return temp[1] === sTemp[1] && temp[2] === sTemp[2]
        })
    }
    return matched
};
const splitSite = (val) => {
    if (val) {
        const temp = val.replace('https://', '').replace('http://')
        return temp.split('/')[0].split('.')
    }
    return []
};
const categoryMatch = (val) => {
    let matched
    if (val && categoryOptions.length > 0) {
        matched = categoryOptions.find(c => val === c.folder)
    }
    return matched
};

const saveTorrent = async (torrent, sub) => {
    const rawTitle = `${sub.title} (${sub.year.split('-')[0]})`;
    const category = categoryMatch(torrent.torrent.category)
    const site = siteMatch(torrent.torrent.address)
    const seasonName = !isNaN(torrent.meta.season) ? `第${torrent.meta.season}季` : null// 季名称
    let episodeName;// 集名称
    if (torrent.meta.isAllEpisodes) {
        episodeName = '全集'
    } else if (torrent.meta.episodes) {
        episodeName =
            torrent.meta.episodes.length === 1 ?
                `第${torrent.meta.episodes[0]}集`
                : `第${torrent.meta.episodes[0]}-${torrent.meta.episodes[torrent.meta.episodes.length - 1]}集`
    }
    const name = rawTitle + (seasonName ? ` ${seasonName}` : '') + (episodeName ? ` ${episodeName}` : '')
    const model = {
        addtime: new Date().getTime(),
        name: name,
        address: torrent.torrent.address,
        siteid: site ? site.id : null,
        categoryid: category ? category.id : null,
        clientid: client ? client.clientId : null,
        status: 'unlink',
        mediaType: sub.mediaType,
        tmdbid: sub.tmdbid,
        season: (sub.mediaType === 'tv' && !isNaN(torrent.meta.season)) ? torrent.meta.season : null,
        libraryid: sub.libraryid,
        autoLink: true,
        toLink: rawTitle //(sub.mediaType === 'tv' && !isNaN(torrent.meta.season)) ? path.join(rawTitle, `S${padNumber(torrent.meta.season, 2)}`) : rawTitle,
    }
    // 解析磁力链接或种子文件
    const torrentInfo = {};
    let downLoadName;
    // 有下载地址，判断是否为磁力链接或种子下载链接
    if (model.address) {
        if (model.address.startsWith('magnet:')) {
            // 1)下载地址为磁力链接，则解析磁力链接
            // A. 解析磁力链接
            const parsed = TorrentInfoParser.magnetLinkInfo(model.address);
            if (parsed) {
                torrentInfo.name = parsed.dn;
                torrentInfo.hash = parsed.infoHash;
            }
        } else if (model.address.startsWith('http://') || model.address.startsWith('https://')) {
            // 2)下载地址为种子下载链接，则先下载种子文件
            if (!model.torrentFile || !await fileExists(path.join(torrentsPath, model.torrentFile))) {
                try {
                    // TODO 考虑重命名种子，可能存在同名种子
                    downLoadName = await download(model.address, torrentsPath);
                    if (!downLoadName) {
                        logger.error('下载种子文件失败: ', model.address)
                        return
                    }
                    if (!downLoadName.endsWith('.torrent')) {
                        // 不是种子文件，删除
                        await fs.delFile(path.join(torrentsPath, downLoadName));
                        logger.error('下载的文件不是种子: ', model.address)
                        return
                    }
                } catch (e) {
                    logger.error('下载种子文件失败', e);
                    return
                }
            }
        }
    }
    // B. 解析种子文件
    const toParseFile = downLoadName || model.torrentFile // downLoadName 表示此次下载的种子文件
    if (toParseFile) {
        const torrentFile = fs.readFileSync(path.join(torrentsPath, toParseFile));
        if (torrentFile) {
            const parsed = TorrentInfoParser.torrentFileInfo(torrentFile);
            if (parsed) {
                torrentInfo.name = parsed.name;
            }
            const torrentHash = await TorrentInfoParser.torrentFileHash(torrentFile);
            if (torrentHash)
                torrentInfo.hash = torrentHash;
        }
    }

    if (!torrentInfo.hash) {
        logger.error('种子解析失败')
        return
    } else {
        // 重命名 防止同名种子
        if (downLoadName) {
            model.torrentFile = `[${torrentInfo.hash}]${downLoadName}`
            fs.renameSync(path.join(torrentsPath, downLoadName), path.join(torrentsPath, model.torrentFile));
        }
    }

    // 新增时判断是否有重复HASH的记录
    if (torrentInfo.hash && await TorrentModel.countBy({ hash_string: torrentInfo.hash })) {
        logger.error('种子已存在')
        return
    }

    // 设置种子信息
    torrentInfo.hash && (model.hashString = torrentInfo.hash)
    torrentInfo.name && (model.btname = torrentInfo.name)

    const inserted = await TorrentModel.insert(model)
    if (inserted && inserted.length > 0) {
        model.id = inserted[0]
        return model
    }
}

// 推送下载
let baseDownloadDir;
async function pushToClient(model, pushOptions) {
    if (!client) {
        logger.info('未配置下载客户端')
        return
    }
    if (!baseDownloadDir) {
        try {
            const setting = await client.getSetting()
            baseDownloadDir = setting.downloadDir
        } catch (e) {
            logger.error('下载客户端连接失败', e)
            return;
        }
    }
    logger.info('推送下载: ', model, pushOptions)
    let pushed = false
    if (model && model.clientid && (model.address || model.torrentFile)) {
        const { startNow = false, enableSiteFolder = true, enableCategoryFolder = true } = pushOptions
        try {
            let downloadDir = baseDownloadDir;
            // 站点目录
            if (enableSiteFolder && model.siteid) {
                const site = await SiteModel.getById(model.siteid)
                if (site && site.folder) {
                    downloadDir = downloadDir.indexOf(path.posix.sep) >= 0 ? 
                        path.posix.join(downloadDir, site.folder) : path.win32.join(downloadDir, site.folder)
                }
            }
            // 分类目录
            if (enableCategoryFolder && model.categoryid) {
                const category = await CategoryModel.getById(model.categoryid)
                if (category && category.folder) {
                    downloadDir = downloadDir.indexOf(path.posix.sep) >= 0 ? 
                        path.posix.join(downloadDir, category.folder) : path.win32.join(downloadDir, category.folder)
                }
            }
            if (model.torrentFile) {
                pushed = await client.addTorrent(path.join(torrentsPath, model.torrentFile), { downloadDir, startNow })
            } else if (model.address) {
                pushed = await client.addMagnet(model.address, { downloadDir, startNow })
            }
        } catch (e) {
            logger.error('推送下载失败', e)
        }
        if (pushed) {
            const downloadInfo = await client.getTorrent(model.hashString)
            const updateModel = {
                btid: downloadInfo.id,
                btname: downloadInfo.name,
                status: 'linked',
            }
            // if (!model.name) updateModel.name = torrent.name
            await TorrentModel.updateBy(updateModel, { id: model.id })
        }

        return pushed
    }
}

module.exports = {
    action, prepare, handelSub,
}